﻿// klipApp.cpp : Defines the entry point for the application.
//

#ifdef WIN32
#define KLIP_WINDOWS
//*
#define KLIP_DIRECTX
/*/
#define KLIP_OPENGL
// */
#else
#define KLIP_LINUX
#define KLIP_OPENGL
#endif

#define KLIP_BOX2D

#include "klip.h"

#include <math.h>

kBox2DController phyController;

class SimpleBox {
private:
	kSprite sprite;

public:
	SimpleBox(kEngine* engine, kView* view, b2World* world, b2BodyType type, float x, float y, float w, float h) : sprite(engine) {
		b2BodyDef bodyDef;
		bodyDef.type = type;
		bodyDef.position = b2Vec2(x, y);

		b2Body* body = world->CreateBody(&bodyDef);
		
		b2PolygonShape shape;
		shape.SetAsBox(w*0.5f, h*0.5f);

		b2FixtureDef fixtureDef;
		fixtureDef.shape = &shape;
		fixtureDef.density = 1.0f;
		fixtureDef.restitution = 0.5f;

		body->CreateFixture(&fixtureDef);

		sprite.setMaterial(kMaterial::getShared()->get(0));
		sprite.setSize(w, h);

		kMat3x3 mat = scale(kVec2(1.02f));
		phyController.addActor(&sprite);
		phyController.setBody(&sprite, body);
		phyController.setLocalTransform(&sprite, mat);

		view->addActor(&sprite);
	}
	~SimpleBox() {
		kMaterial::getShared()->release(sprite.getMaterial());
	}
	inline void setColor(const kColor& color) {
		sprite.setDiffuse(color);
	}
};

class QueryCallback : public b2QueryCallback
{
public:
	QueryCallback(const b2Vec2& point)
	{
		m_point = point;
		m_fixture = NULL;
	}

	bool ReportFixture(b2Fixture* fixture)
	{
		b2Body* body = fixture->GetBody();
		if (body->GetType() == b2_dynamicBody)
		{
			bool inside = fixture->TestPoint(m_point);
			if (inside)
			{
				m_fixture = fixture;

				// We are done, terminate the query.
				return false;
			}
		}

		// Continue the query.
		return true;
	}

	b2Vec2 m_point;
	b2Fixture* m_fixture;
};

int KLIP_MAIN()
{
	kSystemDefault system;

	kWindow* window = system.createWindow(_T("klipApp"));
	window->setSize(kVec2i(800, 600));
	window->setOptions(keWindow_Visible);

	kGraphicsDefault graphics(&system, window);
	kResourcesFiles resources(_T(""));
	kBasicLog log;

	kEngine* engine = kEngine::create(&system, &graphics, &resources, &log);
	{
		b2World world(b2Vec2(0.0f, 10.0f));

		kView view(&graphics);
		kView hud(&graphics);
		kBasicController controller;

		kFont* font = kFont::create(&graphics, _T("example.kfont"));
		kText fpsDisplay(engine);
		fpsDisplay.setFont(font);
		fpsDisplay.setDiffuse(kColor(0xFF00FF00));

		hud.addActor(&fpsDisplay);
		hud.setBackdropEnabled(false);

		view.setBackdrop(kColor(1.0f, 0.0f, 0.0f));

		kImage* backBuffer = graphics.getBackBuffer();

		kImage* explImage = graphics.createImage(_T("explosion.png"), keImage_Drawable);
		kEffect* effect = graphics.createEffect(_T("simple.cgfx"));
		kMaterial material;
		material.setTexture(explImage);
		material.setEffect(effect);

		kSprite* sprite = new kSprite(engine);
		sprite->setMaterial(&material);
		sprite->setSizeFromMaterial();
		sprite->setDepth(-1.0f);
		view.addActor(sprite);
		controller.addActor(sprite);
		controller.setPosition(sprite, sprite->getSize()*0.5f);

		kCamera camera;
		controller.addActor(&camera);
		view.setCamera(&camera);

		int frameIndex = 0;
		
		kMat3x3 m;
		compose(&m, kVec2(), kVec2(50.0f), 0.0f);
		phyController.setGlobalTransform(m);

		SimpleBox myBox(engine, &view, &world, b2_staticBody, 8.0f, 12.0f, 16.0f, 1.0f);
		myBox.setColor(kColor(0.0f, 1.0f, 0.0f));

		for (int i = 0; i < 100; i++) {
			SimpleBox* b = new SimpleBox(engine, &view, &world, b2_dynamicBody, rand()*14.0f/RAND_MAX + 1.0f, rand()*10.0f/RAND_MAX + 1.0f, rand()*1.0f/RAND_MAX + 0.5f, rand()*1.0f/RAND_MAX + 0.5f);
			b->setColor(kColor(0.0f, rand()*1.0f/RAND_MAX, 1.0f));
		}
		
		SimpleBox myBox3(engine, &view, &world, b2_staticBody, 0.0f, -12.0f, 1.0f, 48.0f);
		myBox3.setColor(kColor(1.0f, 0.0f, 1.0f));
		SimpleBox myBox4(engine, &view, &world, b2_staticBody, 16.0f, -12.0f, 1.0f, 48.0f);
		myBox4.setColor(kColor(1.0f, 0.0f, 1.0f));

		b2BodyDef bodyDef;
		b2Body* groundBody = world.CreateBody(&bodyDef);
		b2MouseJoint* mouseJoint = 0;
		window->getMouseInput()->onMouseDown.attach([&](kBase* sender, keMouseButtons buttons) {
			if (buttons == keMouseButton_Left) {
				b2Vec2 p = phyController.convertVec2(kVec2(window->getMouseInput()->getMousePos())*0.02f);

				// Make a small box.
				b2AABB aabb;
				b2Vec2 d;
				d.Set(0.001f, 0.001f);
				aabb.lowerBound = p - d;
				aabb.upperBound = p + d;

				// Query the world for overlapping shapes.
				QueryCallback callback(p);
				world.QueryAABB(&callback, aabb);

				if (callback.m_fixture)
				{
					b2Body* body = callback.m_fixture->GetBody();
					b2MouseJointDef md;
					md.bodyA = groundBody;
					md.bodyB = body;
					md.target = p;
					md.maxForce = 1000.0f * body->GetMass();
					mouseJoint = (b2MouseJoint*)world.CreateJoint(&md);
					body->SetAwake(true);
				}
			}
		});
		window->getMouseInput()->onMouseUp.attach([&](kBase* sender, keMouseButtons buttons) {
			if (buttons == keMouseButton_Left) {
				if (mouseJoint) {
					world.DestroyJoint(mouseJoint);
					mouseJoint = 0;
				}
			}
		});
		window->getMouseInput()->onMouseLeave.attach([&](kBase* sender) {
			if (mouseJoint) {
				world.DestroyJoint(mouseJoint);
				mouseJoint = 0;
			}
		});
		window->getMouseInput()->onMouseMove.attach([&](kBase* sender, const kVec2i& pos) {
			//controller.setPosition(sprite, (kVec2)pos);
			if (mouseJoint) {
				b2Vec2 p = phyController.convertVec2(kVec2(pos)*0.02f);
				mouseJoint->SetTarget(p);
			}
		});


		// Enter the infinite message loop
		while(engine->update())
		{
			float delta = engine->getDeltaTime();

			{
				PERF_SUBQUERY("Physics Step");
				world.Step(1.0f/60.0f, 8, 3);
				phyController.update();
			}

			std::tstringstream fpsStream;
			fpsStream << _T("FPS: ") << engine->getFps();
			fpsDisplay.setText(fpsStream.str());

			controller.setAngle(sprite, controller.getAngle(sprite) + 0.01f);

			view.setViewport(kRect(kVec2i(0, 0), backBuffer->getSize()));
			view.render();
			hud.render();

			// Run game code here
			// ...
			// ...

			if (++frameIndex > 100) {
				kPerformance::save();
				frameIndex = 0;
			}

			Sleep(0);
		}

		font->destroy();
	}
	delete engine;

 	return 0;
}
